#include "WaveFile.h"

#include <string.h>


CWaveFile::CWaveFile()
	{
	IsOpen = false;
	WBuf = NULL;
	BBuf = NULL;
	}


CWaveFile::~CWaveFile()
	{
	Close();
	}


CWaveFile::CWaveFile(const char *FileName, bool AReadOnly)
	{
	IsOpen = false;
	WBuf = NULL;
	BBuf = NULL;
	Open(FileName, AReadOnly);
	}


int CWaveFile::Open(const char *FileName, bool AReadOnly)
	{
	if (IsOpen)
		return ModeError;

	if (!CFile::Open(FileName, AReadOnly ? CFile::modeRead | CFile::shareDenyNone : CFile::modeReadWrite  | CFile::shareDenyWrite))
		return OpenError;

	IsOpen = true;
	ReadOnly = AReadOnly;
	HeaderChanged = false;
	BufSize = 32768;
	BufPos = -1;

	char buf[8];
	WORD w;
	DWORD dw;

	if (Read(buf, 4) != 4)
		goto HeaderErr;
	if (strncmp(buf, "RIFF", 4) != 0)
		goto HeaderErr;

	Seek(4, CFile::current);
	if (Read(buf, 8) != 8)
		goto HeaderErr;
	if (strncmp(buf, "WAVEfmt ", 8) != 0)
		goto HeaderErr;

	Seek(6, CFile::current);
	if (Read((char *) &w, 2) != 2)
		goto HeaderErr;
	switch (w)
		{
		case 1:
			isStereo = false;
			break;
		case 2:
			isStereo = true;
			break;
		default:
			goto HeaderErr;
		}

	if (Read((char *) &dw, 4) != 4)
		goto HeaderErr;
	Freq = dw;
	
	Seek(6, CFile::current);
	if (Read((char *) &w, 2) != 2)
		goto HeaderErr;
	switch (w)
		{
		case 8:
			High = false;
			break;
		case 16:
			High = true;
			break;
		default:
			goto HeaderErr;
		}

	if (Read(buf, 4) != 4)		
		goto HeaderErr;
	if (strncmp(buf, "data", 4) != 0)
		goto HeaderErr;
	if (Read((char *) &nSamples, 4) != 4)
		goto HeaderErr;
	if (nSamples <= 0)
		goto HeaderErr;

	if (High)
		nSamples /= 2;
	if (isStereo)
		nSamples /= 2;

	DataOffs = GetPosition();

	BufPos = -1;

	return OK;

HeaderErr:
	CFile::Close();
	IsOpen = false;
	return FormatError;
	}


inline int WORD2Sample(WORD w)
	{
	if (w & 0x8000)
		return w - 0x10000L;
	else
		return w;
	}


inline int BYTE2Sample(BYTE b)
	{
	if (b & 0x80)
		return (b - 0x100) << 8;
	else
		return b << 8;
	}


int CWaveFile::GetSample(int &x, long Pos, bool Right /* = false*/)
	{
	if (Pos < 0 || Pos >= nSamples)
		return ModeError;

	int res = GetBuf(Pos);
	if (res < 0)
		return res;

	long Offs = isStereo ? ((Pos - BufPos) * 2 + (Right ? 1 : 0)) : (Pos - BufPos);

	if (High)
		x = WORD2Sample(WBuf[Offs]);
	else
		x = BYTE2Sample(BBuf[Offs]);
	return OK;
	}


int CWaveFile::SetLength(long NewLen)
	{
	if (NewLen == nSamples)
		return OK;

	if (NewLen <= 0) 
		return ModeError;

	CFile::SetLength(FileSamplePos(NewLen));
	nSamples = NewLen;
	HeaderChanged = true;
	return OK;
	}

	
int CWaveFile::Create(const char *FileName, long SampleFreq, bool Stereo, int Bits, long Samples)
	{
	// @@@ noch entwickeln
	return ModeError;

/*
*/	
	}

	
int CWaveFile::PutSample(int x, long Pos, bool Right /* = false */)
	{
	// @@@ noch entwickeln
	return ModeError;

/*
	if (!is_open() || Pos >= nSamples)
		return WriteErr;

	if (x > 0x7fff)
		x = 0x7fff;
	else if (x < -0x7fff)
		x = -0x7fff;

	if (High) 
		{
		WORD w;
	
		w = x & 0xffff;
		write((char *) &w, 2);
		}
	else
		{
		unsigned char b;

		b = (x >> 8) & 0xff;
		write((char *) &b, 1);
		}
	Pos++;
	return OK;
	}
*/	
	}

	
void CWaveFile::Close()
	{
	if (!IsOpen)
		return;

	if (!ReadOnly)
		{
		if (BufChanged)
			WriteBuf();
		if (HeaderChanged)
			WriteHeader();
		}
	CFile::Close();
	DeleteBuf();
	IsOpen = false;
	}



int CWaveFile::WriteHeader()
	{
	WORD w;
	DWORD dw;
	long Length;

	if ((Freq != 11025) && (Freq != 22050) && (Freq != 44100))
		return FormatError;

	Length = nSamples * BytePerSample();

	Seek(0L, CFile::begin);

	Write("RIFF", 4);

	dw = Length + 24; 
	Write(&dw, 4);

	Write("WAVEfmt ", 8);

	dw = 16; 
	Write(&dw, 4);
	
	w = 1; 
	Write(&w, 2);

	w = (isStereo ? 2 : 1); 
	Write(&w, 2);

	dw = Freq; 
	Write(&dw, 4);

	dw = Freq * BytePerSample(); 
	Write(&dw, 4);

	w = BytePerSample(); 
	Write(&w, 2);

	w = Bits(); 
	Write((char *) &w, 2);

	Write("data", 4);

	dw = Length; 
	Write((char *) &dw, 4);

	HeaderChanged = false;

	return OK;
	}



void CWaveFile::DeleteBuf()
	{
	if (WBuf != NULL)
		{
		delete[] WBuf;
		WBuf = NULL;
		}
	if (BBuf != NULL)
		{
		delete[] BBuf;
		BBuf = NULL;
		}
	}


bool CWaveFile::CreateBuf()
	{
	if (High)
		{
		if (WBuf == NULL)
			WBuf = new WORD[BufSize / 2];
		return (WBuf != NULL);
		}
	else
		{
		if (BBuf == NULL)
			BBuf = new BYTE[BufSize];
		return (BBuf != NULL);
		}
	}


int CWaveFile::WriteBuf()
	{
	// @@@ noch entwickeln
	return WriteError;
	}


int CWaveFile::GetBuf(long Pos)
	{
	if (CheckBufPos(Pos))
		return true;

	if (!CreateBuf()) // if existing, doesn't matter
		return MemError;
	
	long SamplesInBuf = BufSize / BytePerSample();
	BufPos = (Pos / SamplesInBuf) * SamplesInBuf;

	Seek(FileSamplePos(BufPos), CFile::begin);

	long res = Read(High ? (void *) WBuf : (void *) BBuf, BufSize);
	if (res < BufSize)
		if (res >= (nSamples - BufPos) * BytePerSample())
			return OK;
		else
			return ReadError;
	else
		return OK;
	}

